#ifndef __SOCKET_HEARDERFILE__
#define __SOCKET_HEARDERFILE__
/* header files */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
/* macro definition */
#define MAXLINE     256
#define BACKLOG     4096    // 该参数暗示了内核在开始拒绝连接
                            // 请求之前，队列中排队等待的连接数量
int open_clientfd(const char* ipaddr, const char* port)
{// open client socket file descriptor with getaddrinfo()
 // when function return, client have already connected
 // TCP / IPv4
 
    int clientfd = -1;
    
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;  // TCP
    hints.ai_flags =    AI_ADDRCONFIG |     // use link, return IPv4 addr
                        AI_NUMERICSERV;     // use service or port -> port only
  
    struct addrinfo* resplistp;
    getaddrinfo(ipaddr, port, &hints, &resplistp);

    struct addrinfo* travs; // traverse the linked list
    for(travs = resplistp; travs; travs = travs->ai_next){
        printf("socket.h\\open_clientfd():"
               "try to search socket\n");
        clientfd = socket(travs->ai_family, travs->ai_socktype, travs->ai_protocol);
        if(clientfd < 0){   continue;}
        
        printf("socket.h\\open_clientfd():"
               "try to connect server\n");
        int retcheck = connect(clientfd, travs->ai_addr, travs->ai_addrlen);
        if(retcheck < 0){ close(clientfd);}
        else{   break;}
    }

    // clean up
    freeaddrinfo(resplistp);
    if(travs == NULL){  // search failed
        puts("socket.h\\open_clientfd():"
             "search clientfd failed, exit(1)");
        exit(1);
    }

    assert(clientfd > 0);
    printf("socket.h\\open_clientfd():\n"
           "successfully connect to <server, port> = <%s, %s>\n"
           "clientfd = %d\n\n", 
           ipaddr, port, clientfd);

    return clientfd;
}

int open_listenfd(const char* port)
{
    int listenfd = -1;
    int retcheck;

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;    // TCP
    hints.ai_flags =    AI_PASSIVE |        // accept request from any IP address 
                        AI_ADDRCONFIG |     // when use link, return IPv4 addr 
                        AI_NUMERICSERV;     // use service or port -> port only

    struct addrinfo* resplistp;
    getaddrinfo(NULL, port, &hints, &resplistp);

    struct addrinfo* travs; // traverse the linked list
    for(travs = resplistp; travs; travs = travs->ai_next){
        listenfd = socket(travs->ai_family, travs->ai_socktype, travs->ai_protocol);
        if(listenfd < 0){   continue;}

        int optval = 1;
        retcheck = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&optval, sizeof(optval));
        assert(retcheck >= 0);
        
        retcheck = bind(listenfd, travs->ai_addr, travs->ai_addrlen);
        if(retcheck < 0){ close(listenfd);}
        else{   break;}
    }

    freeaddrinfo(resplistp);
    if(travs == NULL){  // search failed  
        return -1;
    }
    
    assert(listenfd > 0);
    
    retcheck = listen(listenfd, BACKLOG);   // BACKLOG暗示了内核在开始拒绝连接
                                            // 请求之前，队列中排队等待的连接数量
    assert(retcheck >= 0);

    printf("listen.c\\openlistenfd(): "
           "listenfd = %d\n", listenfd);
    
    return listenfd;

}

int server_accept(int listenfd)
{
    int connfd = -1;

    struct sockaddr_storage client_sockaddr;    // 足够放下任意大小的地址
    socklen_t client_sockaddr_len;
    connfd = accept(listenfd, 
                    (struct sockaddr*)&client_sockaddr, 
                    &client_sockaddr_len);
    printf("%d\n", connfd);
    assert(connfd >= 0);

    char client_hostname[MAXLINE];
    char client_port[MAXLINE];
    getnameinfo((struct sockaddr*)&client_sockaddr, client_sockaddr_len, 
                client_hostname, MAXLINE,
                client_port, MAXLINE, 0);
    
    printf("socket.h\\server_accept():\n"
           "server have connected to client <host, port> = <%s, %s>\n\n",
           client_hostname, client_port);

    return connfd;
}



#endif
